Introduction
In this article, we will see how to generate Dynamic Menus (using recursion) in VB.NET based on the XML data which has the complete details about the Menu like Menu Caption and the corresponding Event it has to trigger whenever the user clicks a Menu Item, etc. It's a generalized re-usable component written in such a way that it can be easily plugged to any application based on the requirement.
The main objective of this component is to generate Menus at runtime based on the values present in the XML Configuration file. Let's first have a look at the XML Configuration File assuming the File Name as Menu.xml and sample contents are as follows:
<root>
<TopLevelMenu id="&File">
<MenuItem id = "New" OnClick="_New"/>
<MenuItem id = "Open"/>
<MenuItem id = "Send To">
<MenuItem id ="Mail"/>
<MenuItem id ="My Documents"/>
</MenuItem>
</TopLevelMenu>
<TopLevelMenu id="&Edit">
<MenuItem id = "Copy"/>
<MenuItem id = "Paste" OnClick="_Paste"/>
<MenuItem id = "Clear">
<MenuItem id = "F&ormats"/>
<MenuItem id ="Contents">
<MenuItem id = "Test" OnClick="_Test"/>
</MenuItem>
</MenuItem>
</TopLevelMenu>
</root>
From the above XML, it's evident that Nodes defined as TopLevelMenu
will be the Parent/Top level Menu and the Nodes defined as MenuItem
will be the corresponding child for it. Menu Captions are defined in the attribute named "id
", you can manipulate the XML file to display custom Captions for Menu Items.
- File - is the Top Level Menu
- New - is the child of File
- Open - is the child of File
- SendTo - is the child of File
- Mail - is the child of SendTo
- My Documents - is the child of SendTo
- Edit - is the Top Level Menu
- Copy - is the child of Edit
- Paste - is the child of Edit
- Clear - is the child of Edit
- Formats - is the child of Clear
- Contents - is the child of Clear
- Test - is the child of Contents
Note: The "&" text facilitates the use of short-cut keys for menu items that are being defined.
OnClick
- This is a main attribute which defines the Event the particular menu item should trigger whenever the user performs a click. For example, the New Menu Item has OnClick
attribute defined as below:
<MenuItem id = "New" OnClick="_New"/>
It means that VB.NET Form in which the Menu is getting displayed should have the below code pasted in:
Private Sub MenuItemOnClick_New(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("New Clicked")
End Sub
Please note that Event Name is framed based on the below format.
MenuItemOnClick
- is the hard-coded value in the component, it doesn't come from your XML File.
_New
- is as defined in the OnClick
Attribute. If you change the _New
to _NewItem
(let's say), then your Form Event should also be changed to:
Private Sub MenuItemOnClick_NewItem(ByVal sender As Object, _
ByVal e As System.EventArgs
MessageBox.Show("New Clicked")
End Sub
The dynamic menu component exposes Event Handlers for each and every Menu Item you have created with an attribute value of "OnClick
". If you dont want to create an Event handler for a menu item, then you need not specify the OnClick
attribute for it in the XML file.
Now let us now see the code details of the component which generates Menu dynamically based on the XML content.
DynamicMenu.vb
Public Class DynamicMenu
Private mainMenu As New mainMenu()
Private objXML As Xml.XmlDocument
Private mItem As New MenuItem()
Private objMenu As Menu
Public XMLMenuFile As String
Public objForm As Object
Public Function LoadDynamicMenu()
Dim oXmlElement As Xml.XmlElement
Dim objNode As Xml.XmlNode
objXML = New Xml.XmlDocument()
oXmlElement = CType(objXML.DocumentElement, Xml.XmlElement)
For Each objNode In objXML.FirstChild.ChildNodes
mItem = New MenuItem()
mItem.Text = objNode.Attributes("id").Value
mainMenu.MenuItems.Add(mItem)
GenerateMenusFromXML(objNode, _
mainMenu.MenuItems(mainMenu.MenuItems.Count - 1))
Next
Return objMenu
End Function
Private Sub GenerateMenusFromXML(ByVal objNode As Xml.XmlNode, _
ByVal mItm As MenuItem)
Dim objNod As Xml.XmlNode
Dim sMenu As New MenuItem()
For Each objNod In objNode.ChildNodes
sMenu = New MenuItem()
sMenu.Text = objNod.Attributes("id").Value
mItm.MenuItems.Add(sMenu)
If Not objNod.Attributes("OnClick") Is Nothing Then
FindEventsByName(sMenu, objForm, True, "MenuItemOn", _
objNod.Attributes("OnClick").Value)
End If
GenerateMenusFromXML(objNod, _
mItm.MenuItems(mItm.MenuItems.Count-1))
Next
objMenu = mainMenu
End Sub<
Private Sub FindEventsByName(ByVal sender As Object, _
ByVal receiver As Object, ByVal bind As Boolean, _
ByVal handlerPrefix As String, ByVal handlerSufix As String)
Dim SenderEvents() As System.Reflection.EventInfo =
sender.GetType().GetEvents()
Dim ReceiverType As Type = receiver.GetType()
Dim E As System.Reflection.EventInfo
Dim Method As System.Reflection.MethodInfo
For Each E In SenderEvents
Method = ReceiverType.GetMethod( _
handlerPrefix & E.Name & handlerSufix, _
System.Reflection.BindingFlags.IgnoreCase Or _
System.Reflection.BindingFlags.Instance Or _
System.Reflection.BindingFlags.NonPublic)
If Not Method Is Nothing Then
Dim D As System.Delegate = _
System.Delegate.CreateDelegate(E.EventHandlerType, _
receiver, Method.Name)
If bind Then
E.AddEventHandler(sender, D)
Else
E.RemoveEventHandler(sender, D)
End If
End If
Next
End Sub
End Class
LoadDynamicMenu
is the main method which will gets invoked from VB.NET Form. It’s a function which returns the Menu handle and it should be assigned to the Me.Menu
property.
You can open a New VB.NET Windows Application Project and add the above DynamicMenu.vb to the Project and just paste the below code in Form, it will generate a Dynamic Menu and will associate it to the Form.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim objMenu As New DynamicMenu()
objMenu.XMLMenuFile = "D:\DynamicMenu\Menu.xml"
objMenu.objForm = Me
Me.Menu = objMenu.LoadDynamicMenu()
End Sub
Private Sub MenuItemOnClick_New(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("New Clicked")
End Sub
Private Sub MenuItemOnClick_Paste(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("Paste Clicked")
End Sub
Private Sub MenuItemOnClick_Test(ByVal sender As Object, _
ByVal e As System.EventArgs)
MessageBox.Show("Test Clicked")
End Sub
In the above code, we are creating an object objMenu
for DynamicMenu
Class and we are informing the location of XML Configuration File through XMLMenuFile
property to the DynamicMenu
Class and finally we invoke LoadDynamicMenu()
function which returns the Generated Menu Handle. It will be assigned back to the Me.Menu
property and you are ready to use the Menus.
Just a snapshot of how the generated menus look like in the Form:

The dynamic menu generation logic works in the below way, it basically follows Recursion technique:
- Load the XML document
- Loop the Top Level nodes (in this case, it’s File & Edit)
- Add
MenuItem
for the Top level node (let's say File)
- For each Top level node, loop child nodes (for example, New, Open)
- Add
MenuItem
New/Open to parent File node - Create
EventHandlers
for the newly created Menu Item based on the OnClick
Attribute. - Call step 2a using Recursion logic and drill down to N-levels, i.e., loop till you reach the end node.
- Retrieve the Generated Menu handle
- Assign the generated Menu handle to
Me.Menu
property so as to display the Menu in the Form.
GenerateMenusFromXML
method is invoked recursively for each node to find out whether ChildNodes
exists for each node, if Child Nodes exist, then it gets added as a MenuItem
and further drilled down till it doesn’t return any ChildNodes
.
You can add more elements/nodes to the XML file and you can generate Menu elements based on your requirement. It can support N-levels (File->Open->SubMenu->SubMenu and so on) as it’s a completely dynamic in nature which doesn't have any hardcoding. Since it’s a re-usable component, it can be used across various applications.
References
Please refer to MSDN for more information here.
License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.